Перейти к основному содержимому

Простые приложения на Java

Разработчику Архитектору

Простые приложения на Java

Java представляет собой объектно-ориентированный язык программирования, который широко используется для создания настольных приложений, серверных систем и мобильных решений. Язык отличается строгой типизацией, автоматическим управлением памятью (Garbage Collection) и кроссплатформенностью благодаря виртуальной машине Java (JVM).

В данной главе рассматриваются практические примеры простых консольных приложений. Каждый пример демонстрирует ключевые концепции языка: работу со строками, массивами, коллекциями, файловым вводом-выводом, сетевым взаимодействием и многопоточностью.


Генератор случайных паролей

Это приложение демонстрирует работу с классом String, массивами символов, интерфейсом Random и циклами. Код создает пароль заданной длины, выбирая символы из набора букв, цифр и специальных знаков.

Разбор кода

  • Массивы: Хранение допустимых символов.
  • Random: Генерация индексов для выбора случайного символа.
  • StringBuilder: Эффективное построение итоговой строки без создания множества временных объектов.
import java.util.Random;

public class PasswordGenerator {
public static void main(String[] args) {
int length = 12;
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*";

StringBuilder password = new StringBuilder();
Random random = new Random();

for (int i = 0; i < length; i++) {
int index = random.nextInt(chars.length());
char ch = chars.charAt(index);
password.append(ch);
}

System.out.println("Сгенерированный пароль: " + password.toString());
}
}

Ключевые моменты:

  • Использование StringBuilder вместо конкатенации строк в цикле повышает производительность.
  • Метод nextInt(int bound) возвращает случайное целое число от 0 (включительно) до указанного значения (исключительно).

Сортировщик текстового файла

Приложение читает текст из файла, разбивает его на слова, сортирует их по алфавиту и записывает результат в новый файл. Это пример работы с потоками ввода-вывода (InputStreamReader, BufferedReader) и коллекциями (ArrayList, Collections).

Разбор кода

  • NIO и IO: Классы Files, Paths, FileReader для чтения данных.
  • Коллекции: Список ArrayList для хранения слов.
  • Сортировка: Алгоритм Collections.sort().
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.Collectors;

public class TextSorter {
public static void main(String[] args) {
Path inputPath = Paths.get("input.txt");
Path outputPath = Paths.get("output_sorted.txt");

try {
// Чтение всего содержимого файла
String content = Files.readString(inputPath);

// Разбиение на слова (по пробелам и знакам препинания)
List<String> words = Arrays.stream(content.split("\\s+"))
.filter(word -> !word.isEmpty())
.collect(Collectors.toList());

// Сортировка списка
Collections.sort(words);

// Запись результата
String sortedContent = String.join("\n", words);
Files.writeString(outputPath, sortedContent);

System.out.println("Файл успешно отсортирован. Количество слов: " + words.size());

} catch (IOException e) {
System.err.println("Ошибка при работе с файлом: " + e.getMessage());
}
}
}

Ключевые моменты:

  • Метод Files.readString (доступен в Java 11+) упрощает чтение файлов.
  • Регулярное выражение \\s+ позволяет корректно разделить текст по любому количеству пробельных символов.
  • Обработка исключений через блок try-catch гарантирует безопасность программы при отсутствии файла.

Консольный калькулятор

Простой интерактивный калькулятор, выполняющий арифметические операции (+, -, *, /). Пример демонстрирует работу с пользовательским вводом (Scanner), условными операторами и обработкой ошибок деления на ноль.

Разбор кода

  • Scanner: Получение данных от пользователя.
  • Условная логика: Оператор switch для выбора операции.
  • Типы данных: Переход от String к double для вычислений.
import java.util.Scanner;

public class ConsoleCalculator {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
boolean running = true;

while (running) {
System.out.print("Введите первое число (или 'q' для выхода): ");
String input1 = scanner.nextLine();

if (input1.equalsIgnoreCase("q")) {
break;
}

double num1 = Double.parseDouble(input1);
System.out.print("Выберите операцию (+, -, *, /): ");
String operation = scanner.nextLine();

System.out.print("Введите второе число: ");
double num2 = Double.parseDouble(scanner.nextLine());

double result = 0;
boolean validOperation = true;

switch (operation) {
case "+":
result = num1 + num2;
break;
case "-":
result = num1 - num2;
break;
case "*":
result = num1 * num2;
break;
case "/":
if (num2 == 0) {
System.out.println("Ошибка: Деление на ноль!");
validOperation = false;
} else {
result = num1 / num2;
}
break;
default:
System.out.println("Неверная операция.");
validOperation = false;
}

if (validOperation) {
System.out.printf("Результат: %.2f%n", result);
}
}
System.out.println("Программа завершена.");
scanner.close();
}
}

Характерная черта Java: Строгая проверка типов. Попытка сложить строку и число вызовет ошибку компиляции, что предотвращает многие типы runtime-ошибок.


Трекер задач в JSON

Приложение сохраняет список задач в файл формата JSON и загружает его обратно. Для работы с JSON используется библиотека org.json или встроенные возможности, но в примере ниже показана реализация на основе простого сериализатора вручную или с использованием стандартной библиотеки Jackson (предполагается наличие зависимости). Для автономности примера будет использован простой формат, имитирующий JSON, либо структура класса.

Примечание: В реальных проектах используют Jackson или Gson. Здесь представлен пример с ручным парсингом для демонстрации логики.

Разбор кода

  • POJO (Plain Old Java Object): Класс Task для представления задачи.
  • Файловая система: Создание директорий и запись/чтение файлов.
  • JSON-структура: Представление данных в виде строковой формы.
import java.io.*;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.List;

class Task {
private int id;
private String description;
private boolean completed;

public Task(int id, String description) {
this.id = id;
this.description = description;
this.completed = false;
}

@Override
public String toString() {
return "{\"id\":" + id + ",\"description\":\"" + description + "\",\"completed\":" + completed + "}";
}

// Упрощенный метод для создания объекта из строки (для примера)
public static Task fromJson(String jsonStr) {
// В реальном проекте используйте JsonParser
return null;
}
}

public class TaskTracker {
private static final String FILE_PATH = "tasks.json";
private List<Task> tasks = new ArrayList<>();

public void loadTasks() {
Path path = Paths.get(FILE_PATH);
if (Files.exists(path)) {
try {
String content = Files.readString(path);
// Здесь должна быть логика парсинга JSON строки в объекты Task
System.out.println("Задачи загружены (демо режим).");
} catch (IOException e) {
System.err.println("Ошибка чтения: " + e.getMessage());
}
}
}

public void saveTasks() {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < tasks.size(); i++) {
sb.append(tasks.get(i).toString());
if (i < tasks.size() - 1) sb.append(",");
}
sb.append("]");

try {
Files.writeString(Paths.get(FILE_PATH), sb.toString());
System.out.println("Задачи сохранены.");
} catch (IOException e) {
System.err.println("Ошибка записи: " + e.getMessage());
}
}

public void addTask(String desc) {
int id = tasks.size() > 0 ? tasks.get(tasks.size() - 1).getId() + 1 : 1;
tasks.add(new Task(id, desc));
}

public static void main(String[] args) {
TaskTracker tracker = new TaskTracker();
tracker.loadTasks();

tracker.addTask("Купить продукты");
tracker.addTask("Изучить Java");

tracker.saveTasks();
}
}

Рекомендация: Для продакшн-кода обязательно используйте библиотеку Jackson Databind или Gson. Они берут на себя всю сложность сериализации и десериализации.


Простой HTTP-сервер и клиент

Java предоставляет возможности для создания сетевых приложений через пакет java.net. Ниже приведен пример минимального сервера, слушающего порт, и клиента, отправляющего запрос.

Разбор кода

  • ServerSocket: Объект для прослушивания входящих соединений.
  • Socket: Объект для установления соединения.
  • Потоки ввода/вывода: InputStream, OutputStream для передачи данных.
// --- Сервер ---
import java.io.*;
import java.net.*;

public class SimpleHttpServer {
public static void main(String[] args) throws IOException {
int port = 8080;
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("Сервер запущен на порту " + port);

while (true) {
Socket clientSocket = serverSocket.accept();
handleClient(clientSocket);
}
}

private static void handleClient(Socket socket) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

String requestLine = in.readLine();
System.out.println("Запрос: " + requestLine);

String response = "HTTP/1.1 200 OK\r\n" +
"Content-Type: text/plain\r\n" +
"\r\n" +
"Hello from Java Server!";

out.println(response);
socket.close();
}
}

// --- Клиент ---
import java.io.*;
import java.net.*;

public class HttpClient {
public static void main(String[] args) throws IOException {
URL url = new URL("http://localhost:8080");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

connection.setRequestMethod("GET");

int status = connection.getResponseCode();
System.out.println("Статус ответа: " + status);

BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();

while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();

System.out.println("Ответ сервера: " + response.toString());
}
}

Характерная черта Java: Встроенная поддержка TCP/IP на низком уровне. Не требуется подключение внешних драйверов для базовой сетевой коммуникации.


Отправитель HTTP-запросов (REST API)

Этот пример расширяет функциональность клиента, позволяя отправлять POST-запросы с данными в формате JSON. Используется HttpURLConnection для настройки заголовков и отправки тела запроса.

import java.io.*;
import java.net.*;

public class RestApiClient {
public static void main(String[] args) throws IOException {
String apiUrl = "https://jsonplaceholder.typicode.com/posts";
String jsonInput = "{ \"title\": \"foo\", \"body\": \"bar\", \"userId\": 1 }";

URL url = new URL(apiUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();

conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");

try (OutputStream os = conn.getOutputStream()) {
byte[] input = jsonInput.getBytes("utf-8");
os.write(input, 0, input.length);
}

int responseCode = conn.getResponseCode();
System.out.println("Отправлен запрос. Код ответа: " + responseCode);

if (responseCode == HttpURLConnection.HTTP_CREATED) {
System.out.println("Ресурс успешно создан.");
}

conn.disconnect();
}
}

Утилита для сканирования директорий

Приложение обходит структуру папок и выводит список всех файлов с указанием их размера и пути. Используется рекурсивный обход или класс Files.walk.

Разбор кода

  • Files.walk: Поток путей, представляющий дерево директорий.
  • Фильтрация: Исключение скрытых файлов или определенных расширений.
import java.io.IOException;
import java.nio.file.*;
import java.util.stream.Stream;

public class DirectoryScanner {
public static void main(String[] args) {
Path startPath = Paths.get("."); // Текущая директория

try (Stream<Path> stream = Files.walk(startPath)) {
stream
.filter(Files::isRegularFile) // Только файлы
.forEach(path -> {
long size = 0;
try {
size = Files.size(path);
} catch (IOException e) {
// Игнорирование ошибок доступа
}
System.out.printf("%s (%d байт)%n", path, size);
});
} catch (IOException e) {
System.err.println("Ошибка сканирования: " + e.getMessage());
}
}
}

Скрипт для создания резервного копирования файлов

Приложение копирует файлы из одной директории в другую с добавлением временной метки к имени файла. Это демонстрирует работу с StandardCopyOption и манипуляцию именами файлов.

import java.io.IOException;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class BackupUtility {
public static void main(String[] args) {
Path sourceDir = Paths.get("source_data");
Path backupDir = Paths.get("backup_data");

try {
// Создание директории бэкапа, если её нет
Files.createDirectories(backupDir);

LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss");
String timestamp = now.format(formatter);

// Поиск всех файлов в источнике
try (var stream = Files.list(sourceDir)) {
stream.forEach(file -> {
try {
String fileName = file.getFileName().toString();
String nameWithoutExt = fileName.substring(0, fileName.lastIndexOf('.'));
String ext = fileName.substring(fileName.lastIndexOf('.'));

Path newPath = backupDir.resolve(nameWithoutExt + "_" + timestamp + ext);

Files.copy(file, newPath, StandardCopyOption.REPLACE_EXISTING);
System.out.println("Скопировано: " + fileName + " -> " + newPath.getFileName());
} catch (Exception e) {
System.err.println("Ошибка копирования " + file + ": " + e.getMessage());
}
});
}
} catch (IOException e) {
System.err.println("Критическая ошибка: " + e.getMessage());
}
}
}

Мониторинг дискового пространства

Утилита отображает информацию о свободном и занятом месте на диске. Используется класс FileStore из пакета java.nio.file.

import java.io.File;
import java.io.IOException;
import java.nio.file.*;

public class DiskSpaceMonitor {
public static void main(String[] args) {
File root = new File("/"); // Корневая директория

try {
FileStore store = FileSystems.getDefault().getFileStore(root.toPath());

long totalSize = store.getTotalSpace();
long usableSize = store.getUsableSpace();
long usedSize = totalSize - usableSize;

System.out.println("Диск: " + root.getAbsolutePath());
System.out.println("Общий размер: " + formatBytes(totalSize));
System.out.println("Занято: " + formatBytes(usedSize));
System.out.println("Свободно: " + formatBytes(usableSize));
System.out.println("Процент использования: " + (totalSize > 0 ? (usedSize * 100 / totalSize) : 0) + "%");

} catch (IOException e) {
System.err.println("Ошибка получения информации о диске: " + e.getMessage());
}
}

private static String formatBytes(long bytes) {
if (bytes < 1024) return bytes + " B";
if (bytes < 1024 * 1024) return bytes / 1024 + " KB";
if (bytes < 1024 * 1024 * 1024) return bytes / (1024 * 1024) + " MB";
return bytes / (1024L * 1024 * 1024) + " GB";
}
}

Парсер URL и проверка доступности ресурса

Приложение принимает URL, разбирает его компоненты (протокол, хост, путь) и проверяет, доступен ли ресурс по HTTP.

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;

public class UrlChecker {
public static void main(String[] args) {
String urlString = "https://example.com";

try {
URL url = new URL(urlString);

System.out.println("Анализ URL: " + urlString);
System.out.println("Протокол: " + url.getProtocol());
System.out.println("Хост: " + url.getHost());
System.out.println("Порт: " + url.getPort());
System.out.println("Путь: " + url.getPath());

HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("HEAD"); // HEAD запрос быстрее GET
connection.setConnectTimeout(5000); // 5 секунд таймаут
connection.connect();

int statusCode = connection.getResponseCode();
System.out.println("Статус кода: " + statusCode);

if (statusCode >= 200 && statusCode < 300) {
System.out.println("Ресурс доступен.");
} else {
System.out.println("Ресурс недоступен или вернул ошибку.");
}

connection.disconnect();

} catch (UnknownHostException e) {
System.out.println("Ошибка: Хост не найден.");
} catch (IOException e) {
System.out.println("Ошибка сети: " + e.getMessage());
}
}
}

Конвертер форматов дат

Работа с датами в Java требует внимания к деталям. Приведен пример конвертации между старым форматом SimpleDateFormat (для совместимости) и новым API java.time (рекомендуемый стандарт с Java 8).

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

public class DateConverter {
public static void main(String[] args) {
String dateString = "2026-05-06 14:30:00";
String inputPattern = "yyyy-MM-dd HH:mm:ss";
String outputPattern = "dd/MM/yyyy HH:mm";

DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern(inputPattern);
DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern(outputPattern);

try {
LocalDateTime dateTime = LocalDateTime.parse(dateString, inputFormatter);

// Конвертация в другой формат
String formattedDate = dateTime.format(outputFormatter);

System.out.println("Исходная строка: " + dateString);
System.out.println("Преобразованная строка: " + formattedDate);

// Пример получения даты сегодня
LocalDate today = LocalDate.now();
System.out.println("Сегодня: " + today);

} catch (DateTimeParseException e) {
System.out.println("Неверный формат даты: " + e.getMessage());
}
}
}

Утилита для просмотра запущенных процессов

Приложение выводит список активных процессов операционной системы с их идентификаторами (PID) и названиями. В Java для этого используется класс ProcessHandle.

import java.util.Comparator;
import java.util.stream.Stream;

public class ProcessViewer {
public static void main(String[] args) {
System.out.println("Список запущенных процессов:");
System.out.println("PID\t\tName");
System.out.println("---------------------------");

// Stream API для обработки процесса
try (Stream<ProcessHandle> processes = ProcessHandle.allProcesses()) {
processes
.sorted(Comparator.comparingLong(ProcessHandle::pid))
.forEach(process -> {
long pid = process.pid();
String name = process.info().command().orElse("N/A");

// Ограничение вывода первых 100 символов имени
if (name.length() > 100) {
name = name.substring(0, 97) + "...";
}

System.out.printf("%d\t%s%n", pid, name);
});
}
}
}

Характерная черта Java: Класс ProcessHandle предоставляет безопасный и удобный способ взаимодействия с процессами ОС без необходимости вызова нативных библиотек.